#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <semaphore.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <math.h>
#include <sys/prctl.h>

#include <constraints.h>
#include <imp/imp_ivs.h>

#include <system/system.h>
#include <system/module.h>
#include <system/group.h>
#include <system/device.h>
#include <system/vbm.h>

#include <imp/imp_log.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define TAG "IVS"

//#define IVS_PROCESS_RANDOM_DELAY
#define MAX_PROCESS_DELAY_MS		2000
//#define SHOW_MODULE_TIME

typedef struct {
	sem_t			sem_process_start;
	sem_t			sem_process_end;
	sem_t			sem_process_result;
	int				enable;
	IMPIVSInterface *handler;
	bool			set_param_flag;
	int				group_index;
	IMPFrameInfo	*frame;
	pthread_t		pid;
} IVSChn;

typedef struct {
	int			enable;
} IVSGrp;

typedef struct {
	Device			*dev;
	IVSChn			chn[NR_MAX_IVS_CHANNEL];
	IVSGrp			grp[NR_MAX_IVS_GROUPS];
} IVS;

static IVS *gIVS;

static IVS *GetIVS(void )
{
	return gIVS;
}

static void SetIVS(IVS *ivs)
{
	gIVS = ivs;
}

static IVS *ivs_create_device(void)
{
	IVS *ivs;
	Device *dev;

	dev = alloc_device("IVS", sizeof(IVS));
	if (dev == NULL) {
		IMP_LOG_ERR(TAG, "alloc_device() error\n");
		return NULL;
	}

	dev->dev_id = DEV_ID_IVS;
	dev->nr_groups = NR_MAX_IVS_GROUPS;

	ivs = device_pri(dev);
	ivs->dev = dev;

	return ivs;
}

static int ivs_destory_device(IVS *ivs)
{
	if(ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_destory_device error !\n");
		return -1;
	}

	Device *dev = ivs->dev;

	free_device(dev);

	return 0;
}

void ivs_processing_cleanup_push_handle(void *arg)
{

	IVSChn *chn = arg;
	if (chn->frame) {
		IMP_LOG_DBG(TAG, "%s(%d)\n", __func__, __LINE__);
		VBMUnlockFrameByVaddr(chn->frame->virAddr);
	}
}

void* ivs_processing(void *p)
{
	IVS *ivs;
	int i = (int )p, ret = 0, errnum = -1, result_value = 0;
	IMPIVSInterface         *handler;
	struct timespec ts;
#ifdef SHOW_MODULE_TIME
	int64_t start_time_raw = 0, end_time_raw = 0;
	int64_t start_time_thread = 0, end_time_thread = 0;
#endif

	char thread_name[64];
	sprintf(thread_name, "IVS(%d)-%s", i, __func__);
	prctl(PR_SET_NAME, (unsigned long)thread_name);

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return NULL;
	}

	while(1) {
		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
		if (ivs->chn[i].frame == NULL) {
			pthread_cleanup_push(ivs_processing_cleanup_push_handle, &ivs->chn[i]);
			pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
			sem_wait(&ivs->chn[i].sem_process_start);
			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
			pthread_cleanup_pop(0);
		} else {
			ret = 0;
			while(ivs->chn[i].enable == 1) {
				clock_gettime(CLOCK_REALTIME, &ts);
				ts.tv_sec += ((int64_t)MAX_PROCESS_DELAY_MS * 1000000 + ts.tv_nsec) / 1000000000;
				ts.tv_nsec = ((int64_t)MAX_PROCESS_DELAY_MS * 1000000 + ts.tv_nsec) % 1000000000;
				ret = sem_timedwait(&ivs->chn[i].sem_process_start, &ts);
				if (ret < 0) {
					errnum = errno;
					IMP_LOG_WARN(TAG, ":warn: sem_timedwait %d ret = %d, errnum = %d, enable = %d\n",
							__LINE__, ret, errnum, ivs->chn[i].enable);
				}
				if (((ret < 0) && (errnum != ETIMEDOUT) && (errnum != EINTR)) || (ret == 0)) {
					break;
				}
			}
			if ((ivs->chn[i].enable != 1) || ((ret < 0) && (errnum != ETIMEDOUT) && (errnum != EINTR))) {
				if ((ivs->chn[i].enable != 1) && (ret >= 0) && (ivs->chn[i].frame->virAddr != 0)) {
					IMP_LOG_INFO(TAG, "%s(%d):unlock has post frame(pool_idx=%d,idx=%d),ret=%d\n",
							__func__, __LINE__, ivs->chn[i].frame->pool_idx, ivs->chn[i].frame->index, ret);
					VBMUnlockFrameByVaddr((uint32_t)(ivs->chn[i].frame->virAddr));
				}
				pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

				if (ivs->chn[i].enable != 1) {
					IMP_LOG_WARN(TAG, "warn: %d ivs->chn[%d].enable = %d\n",
							__LINE__, i, ivs->chn[i].enable);
					ivs->chn[i].frame = NULL;
					continue;
				} else {
					IMP_LOG_ERR(TAG, "%s(%d):ivs chn[%d].enable=%d is stoped, ret=%d, errnum=%d\n", __func__, __LINE__, i, ivs->chn[i].enable, ret, errnum);
					return NULL;
				}
			}
		}
		handler = ivs->chn[i].handler;
#ifdef SHOW_MODULE_TIME
		/* Mark the start time */
		start_time_raw = system_gettime(RAW);
		start_time_thread = system_gettime(THREAD);
		end_time_raw = end_time_raw > 0 ? end_time_raw : start_time_raw;
		end_time_thread = end_time_thread > 0 ? end_time_thread : start_time_thread;
		/* Show module gop thread time cost */
#if 1
		IMP_LOG_DBG(TAG, "line:%d, ivs[%d] elapsed:%llu usec, active:%llu usec\n", __LINE__, i,
				start_time_raw - end_time_raw, start_time_thread - end_time_thread);
#else
		printf("line:%d, ivs[%d] elapsed:%llu usec, active:%llu usec\n", __LINE__, i,
				start_time_raw - end_time_raw, start_time_thread - end_time_thread);
#endif
#endif
		/* ret:0->checked ok, 1->skiped ok, -1->checked err */
        if (handler->processAsync) {
            ret = handler->processAsync(handler, ivs->chn[i].frame);
            if (ret < 0) {
                pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
                IMP_LOG_ERR(TAG, "%s(%d): chnNum=%d ivs process failed\n", __func__, __LINE__, i);
                return NULL;
            }
        } else {
            VBMUnlockFrameByVaddr((uint32_t)(ivs->chn[i].frame->virAddr));
        }
		ivs->chn[i].frame = NULL;
#ifdef SHOW_MODULE_TIME
		/* Mark the end time */
		end_time_raw = system_gettime(RAW);
		end_time_thread = system_gettime(THREAD);
#if 1
		IMP_LOG_DBG(TAG, "line:%d, ivs[%d] elapsed:%llu usec, active:%llu usec\n", __LINE__, i,
				end_time_raw - start_time_raw, end_time_thread - start_time_thread);
#else
		printf("line:%d, ivs[%d] elapsed:%llu usec, active:%llu usec\n", __LINE__, i,
				end_time_raw - start_time_raw, end_time_thread - start_time_thread);
#endif
#endif
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
#ifdef IVS_PROCESS_RANDOM_DELAY
		usleep(rand() % (MAX_PROCESS_DELAY_MS * 1000));
#endif
		sem_getvalue(&ivs->chn[i].sem_process_result, &result_value);
		if (ret == 0 && result_value == 0) {
			sem_post(&ivs->chn[i].sem_process_result);
		}
		sem_post(&ivs->chn[i].sem_process_end);
	}

	return NULL;
}

static int ivs_update(Group *group, IMPFrameInfo *frame)
{
	int i, ret = -1, errnum = -1;
	IVS *ivs;
	struct timespec ts;

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	for(i = 0; i < NR_MAX_IVS_CHANNEL; i++) {
#ifndef CONFIG_FORCE_USE_CONTINUE_FRAME
		int sem_end_val = 0;
#endif
		if (ivs->chn[i].group_index != group->group_index) {
			continue;
		}

		if (ivs->chn[i].enable != 1) {
			continue;
		}
#ifndef CONFIG_FORCE_USE_CONTINUE_FRAME
		if (sem_getvalue(&ivs->chn[i].sem_process_end, &sem_end_val) < 0) {
			IMP_LOG_ERR(TAG, "sem_getvalue(&ivs->chn[%d].sem_process_end) failed\n", i);
			return -1;
		}
		if (sem_end_val <= 0) {
			continue;
		}
#endif
		while(ivs->chn[i].enable == 1) {
			clock_gettime(CLOCK_REALTIME, &ts);
			ts.tv_sec += ((int64_t)MAX_PROCESS_DELAY_MS * 1000000 + ts.tv_nsec) / 1000000000;
			ts.tv_nsec = ((int64_t)MAX_PROCESS_DELAY_MS * 1000000 + ts.tv_nsec) % 1000000000;
			ret = sem_timedwait(&ivs->chn[i].sem_process_end, &ts);
			if (ret < 0) {
				errnum = errno;
				IMP_LOG_WARN(TAG, "warn: sem_timedwait %d ret = %d errnum = %d\n",
						__LINE__, ret, errnum);
			}
			if ((ret == 0) || ((ret < 0) && (errnum != ETIMEDOUT) && (errnum != EINTR))) {
				break;
			}
		}

		if ((ivs->chn[i].enable != 1) || ((ret < 0) && (errnum != ETIMEDOUT) && (errnum != EINTR))) {
			IMP_LOG_WARN(TAG, "ivs chn[%d] may stoped or error happen errnum = %d\n", i, errnum);
			continue;
		}

		if (ivs->chn[i].set_param_flag == true) {
			ivs->chn[i].set_param_flag = false;
			if (ivs->chn[i].handler->setParam) {
				ivs->chn[i].handler->setParam(ivs->chn[i].handler, ivs->chn[i].handler->param);
			}
		}

		/* ret:>=0 -> check ok, -1->checked err */
		if (ivs->chn[i].handler->preProcessSync) {
			ret = ivs->chn[i].handler->preProcessSync(ivs->chn[i].handler, frame);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "ivs chn[%d] PreprocessSync failed\n", i);
				continue;
			}
		}

		VBMLockFrameByVaddr(frame->virAddr);
		ivs->chn[i].frame = frame;
		sem_post(&ivs->chn[i].sem_process_start);
	}

	for (i = 0; i < group->nr_outputs; i++) {
		group->for_output_data[i] = frame;
	}

	return 0;
}

static int ivs_create_group(int group_index, IVS *ivs)
{
	Group *grp;
	Device *dev;
	char grp_name[MAX_MODULE_NAME_LEN];

	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	dev = ivs->dev;
	if ((group_index > dev->nr_groups - 1) || (group_index < 0)) {
		IMP_LOG_ERR(TAG, "Invalid group num%d\n", group_index);
		return -1;
	}

	sprintf(grp_name, "%s-%d", dev->name, group_index);

	grp = create_group(dev->dev_id, group_index, grp_name, ivs_update);
	grp->device = dev;
	dev->groups[group_index] = grp;
	grp->nr_outputs = 1;

	return 0;
}

static int ivs_destroy_group(int group_index, IVS *ivs)
{
	Group *grp;
	Device *dev;

	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	dev = ivs->dev;
	if ((group_index > dev->nr_groups - 1) || (group_index < 0)) {
		IMP_LOG_ERR(TAG, "Invalid group num%d\n\n", group_index);
		return -1;
	}

	grp = dev->groups[group_index];
	if (grp == NULL) {
		IMP_LOG_WARN(TAG, "group-%d has not been created\n", group_index);
		return -1;
	}

	destroy_group(grp, dev->dev_id);

	dev->groups[group_index] = NULL;

	return 0;
}
/*
 * init function
 */

int IVSInit(void )
{
	IVS *ivs;
	int i;

	ivs = ivs_create_device();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_dev failed\n");
		return -1;
	}

	for(i = 0; i < NR_MAX_IVS_GROUPS; i++) {
		ivs->grp[i].enable = 0;
	}

	for(i = 0; i < NR_MAX_IVS_CHANNEL; i++) {
		ivs->chn[i].enable = 0;
		ivs->chn[i].handler = NULL;
		ivs->chn[i].group_index = -1;
	}

	SetIVS(ivs);

	return 0;
}

/*
 * exit function
 */

int IVSExit(void)
{
	IVS *ivs;

	ivs = GetIVS();
	if (ivs) {
		ivs_destory_device(ivs);
		SetIVS(NULL);
	}

	return 0;
}

/*
 * start API
 */

int IMP_IVS_CreateGroup(int GrpNum)
{
	int ret;
	IVS *ivs;

	if((GrpNum < 0) || (GrpNum > NR_MAX_IVS_GROUPS)) {
		IMP_LOG_ERR(TAG, "GrpNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "IVS may not init\n");
		return -1;
	}

	if(ivs->grp[GrpNum].enable == 1) {
		IMP_LOG_VERBOSE(TAG, "IMP_IVS_CreateGroup(%d) had been used !\n", GrpNum);
	} else {
		ret = ivs_create_group(GrpNum, ivs);
		if(ret < 0) {
			IMP_LOG_ERR(TAG, "ivs_create_group(%d) error !", GrpNum);
			return -1;
		}
	}

	ivs->grp[GrpNum].enable = 1;

	return 0;
}

int IMP_IVS_DestroyGroup(int GrpNum)
{
	int ret;
	IVS *ivs;

	if((GrpNum < 0) || (GrpNum > NR_MAX_IVS_GROUPS)) {
		IMP_LOG_ERR(TAG, "GrpNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "IVS may not init\n");
		return -1;
	}

	if(ivs->grp[GrpNum].enable == 0) {
		IMP_LOG_VERBOSE(TAG, "IMP_IVS_CreateGroup(%d) had been not used !\n", GrpNum);
	} else {
		ret = ivs_destroy_group(GrpNum, ivs);
		if(ret < 0) {
			IMP_LOG_ERR(TAG, "ivs_destroy_group(%d) error !", GrpNum);
			return -1;
		}
	}

	ivs->grp[GrpNum].enable = 0;

	return 0;
}

int IMP_IVS_CreateChn(int ChnNum, IMPIVSInterface *handler)
{
    int ret = 0;
    IVS *ivs = NULL;

    if((ChnNum > NR_MAX_IVS_CHANNEL) || (ChnNum < 0)) {
        IMP_LOG_ERR(TAG, "ChnNum is error !\n");
        return -1;
    }

    if(handler == NULL) {
        IMP_LOG_ERR(TAG, "IMPIVSInterface is error, handler is null!\n");
        return -1;
    }

    if(((handler->param == NULL) && (handler->paramSize != 0)) || ((handler->param != NULL) && (handler->paramSize == 0))) {
        IMP_LOG_ERR(TAG, "IMPIVSInterface is error, param(%p) not match paramSize(%d)\n", handler->param, handler->paramSize);
        return -1;
    }

    if((handler->preProcessSync == NULL) && (handler->processAsync == NULL)) {
        IMP_LOG_ERR(TAG, "IMPIVSInterface is error, preProcessSync(%p) and processAsync(%p) must be used at lest one\n",
                handler->preProcessSync, handler->processAsync);
        return -1;
    }

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	if(ivs->chn[ChnNum].handler != NULL) {
		IMP_LOG_ERR(TAG, "IMP_IVS_CreateChn(%d) has been used !\n", ChnNum);
		return -1;
	}

	if (sem_init(&ivs->chn[ChnNum].sem_process_start, 0, 0) < 0) {
		IMP_LOG_ERR(TAG, "sem_init ivs->chn[%d].sem_process_start failed\n", ChnNum);
		goto err_sem_init_process_start;
	}

	if (sem_init(&ivs->chn[ChnNum].sem_process_end, 0, 1) < 0) {
		IMP_LOG_ERR(TAG, "sem_init ivs->chn[%d].sem_process_end failed\n", ChnNum);
		goto err_sem_init_process_end;
	}

	if (sem_init(&ivs->chn[ChnNum].sem_process_result, 0, 0) < 0) {
		IMP_LOG_ERR(TAG, "sem_init ivs->chn[%d].sem_process_end failed\n", ChnNum);
		goto err_sem_init_process_result;
	}

#ifdef IVS_PROCESS_RANDOM_DELAY
	srand(time(0));
#endif

	ivs->chn[ChnNum].enable = 0;
	ivs->chn[ChnNum].handler = handler;
	ivs->chn[ChnNum].set_param_flag = false;

	if (ivs->chn[ChnNum].handler->init && (ivs->chn[ChnNum].handler->init(ivs->chn[ChnNum].handler) < 0)) {
		IMP_LOG_ERR(TAG, "ivs->chn[%d].handler->init failed\n", ChnNum);
		goto err_ivs_handler_init;
	}

	ret = pthread_create(&ivs->chn[ChnNum].pid, NULL, ivs_processing, (void *)ChnNum);
	if(ret < 0) {
		IMP_LOG_ERR(TAG, "pthread_create(%d) error !", ChnNum);
		goto err_pthread_ivs_process;
	}

	return 0;

err_pthread_ivs_process:
    if (ivs->chn[ChnNum].handler->exit) {
        ivs->chn[ChnNum].handler->exit(ivs->chn[ChnNum].handler);
    }
err_ivs_handler_init:
	ivs->chn[ChnNum].enable = 0;
	ivs->chn[ChnNum].handler = NULL;
	ivs->chn[ChnNum].set_param_flag = false;
	sem_destroy(&ivs->chn[ChnNum].sem_process_result);
err_sem_init_process_result:
	sem_destroy(&ivs->chn[ChnNum].sem_process_end);
err_sem_init_process_end:
	sem_destroy(&ivs->chn[ChnNum].sem_process_start);
err_sem_init_process_start:
	return -1;
}

int IMP_IVS_DestroyChn(int ChnNum)
{
	IVS *ivs;

	if((ChnNum > NR_MAX_IVS_CHANNEL) || (ChnNum < 0)) {
		IMP_LOG_ERR(TAG, "ChnNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	if(ivs->chn[ChnNum].handler == NULL)
		IMP_LOG_VERBOSE(TAG, "IMP_IVS_DestroyChn(%d) has been not used !\n", ChnNum);

	pthread_cancel(ivs->chn[ChnNum].pid);
	pthread_join(ivs->chn[ChnNum].pid, NULL);

    if (ivs->chn[ChnNum].handler->exit) {
        ivs->chn[ChnNum].handler->exit(ivs->chn[ChnNum].handler);
    }

	ivs->chn[ChnNum].enable = 0;
	ivs->chn[ChnNum].handler = NULL;
	ivs->chn[ChnNum].frame = NULL;
	ivs->chn[ChnNum].pid = -1;

	sem_destroy(&ivs->chn[ChnNum].sem_process_result);
	sem_destroy(&ivs->chn[ChnNum].sem_process_end);
	sem_destroy(&ivs->chn[ChnNum].sem_process_start);

	return 0;
}

int IMP_IVS_RegisterChn(int GrpNum, int ChnNum)
{
	IVS *ivs;

	if((GrpNum < 0) || (GrpNum > NR_MAX_IVS_GROUPS)) {
		IMP_LOG_ERR(TAG, "GrpNum is error !\n");
		return -1;
	}

	if((ChnNum > NR_MAX_IVS_CHANNEL) || (ChnNum < 0)) {
		IMP_LOG_ERR(TAG, "ChnNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	if(ivs->chn[ChnNum].handler == NULL) {
		IMP_LOG_ERR(TAG, "%s:not init IMP_IVS_CreateChn!\n", __func__);
		return -1;
	}

	if(ivs->chn[ChnNum].group_index != -1)
		IMP_LOG_VERBOSE(TAG, "IMP_IVS_RegisterChn(%d,%d) has been used !\n", GrpNum, ChnNum);

	ivs->chn[ChnNum].group_index = GrpNum;

	return 0;
}

int IMP_IVS_UnRegisterChn(int ChnNum)
{
	IVS *ivs;

	if((ChnNum > NR_MAX_IVS_CHANNEL) || (ChnNum < 0)) {
		IMP_LOG_ERR(TAG, "ChnNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	if(ivs->chn[ChnNum].handler == NULL) {
		IMP_LOG_ERR(TAG, "%s:not init IMP_IVS_CreateChn!\n", __func__);
		return -1;
	}

	if(ivs->chn[ChnNum].group_index == -1)
		IMP_LOG_VERBOSE(TAG, "IMP_IVS_UnRegisterChn(%d) has been not used !\n", ChnNum);

	ivs->chn[ChnNum].group_index = -1;

	return 0;
}

int IMP_IVS_StartRecvPic(int ChnNum)
{
	IVS *ivs;

	if((ChnNum > NR_MAX_IVS_CHANNEL) || (ChnNum < 0)) {
		IMP_LOG_ERR(TAG, "ChnNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	if(ivs->chn[ChnNum].handler == NULL) {
		IMP_LOG_ERR(TAG, "%s:not init IMP_IVS_CreateChn!\n", __func__);
		return -1;
	}

	if(ivs->chn[ChnNum].enable == 1)
		IMP_LOG_VERBOSE(TAG, "IMP_IVS_StartRecvPic(%d) has been used !\n", ChnNum);

	ivs->chn[ChnNum].enable = 1;

	return 0;
}

int IMP_IVS_StopRecvPic(int ChnNum)
{
	int sem_process_end_val = 0, sem_sync_cnt = 0;
	IVS *ivs;

	if((ChnNum > NR_MAX_IVS_CHANNEL) || (ChnNum < 0)) {
		IMP_LOG_ERR(TAG, "ChnNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	if (ivs->chn[ChnNum].handler == NULL) {
		IMP_LOG_ERR(TAG, "%s:not init IMP_IVS_CreateChn!\n", __func__);
		return -1;
	}

	if (ivs->chn[ChnNum].enable == 0)
		IMP_LOG_VERBOSE(TAG, "IMP_IVS_StopRecvPic(%d) has been not used !\n", ChnNum);

	ivs->chn[ChnNum].enable = 0;

	do {
		sem_getvalue(&ivs->chn[ChnNum].sem_process_end, &sem_process_end_val);
		if ((sem_process_end_val > 0) && ivs->chn[ChnNum].handler->flushFrame) {
			ivs->chn[ChnNum].handler->flushFrame(ivs->chn[ChnNum].handler);
			break;
		}
		sem_sync_cnt++;
	} while (ivs->chn[ChnNum].handler->flushFrame && (sem_process_end_val <= 0) && (usleep(10000) == 0) && (sem_sync_cnt < 100));

	if (sem_sync_cnt >= 100) {
		IMP_LOG_WARN(TAG, "IMP_IVS_StopRecvPic(%d) hasn't sync the sem_process_end signal, force stoped\n", ChnNum);
	}

	return 0;
}

int IMP_IVS_PollingResult(int ChnNum, int timeoutMs)
{
	int ret;
	IVS *ivs;
	struct timespec ts;

	if((ChnNum > NR_MAX_IVS_CHANNEL) || (ChnNum < 0)) {
		IMP_LOG_ERR(TAG, "ChnNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	if(ivs->chn[ChnNum].handler == NULL) {
		IMP_LOG_ERR(TAG, "%s:not init IMP_IVS_CreateChn!\n", __func__);
		return -1;
	}

	if (timeoutMs == 0) {
		ret = sem_trywait(&ivs->chn[ChnNum].sem_process_result);
	} else {
		if (timeoutMs < 0) {
			ts.tv_sec  = time(NULL) + 10;
			ts.tv_nsec = 0;
		} else {
			clock_gettime(CLOCK_REALTIME, &ts);
			ts.tv_sec += ((int64_t)timeoutMs * 1000000 + ts.tv_nsec) / 1000000000;
			ts.tv_nsec = ((int64_t)timeoutMs * 1000000 + ts.tv_nsec) % 1000000000;
		}
		ret = sem_timedwait(&ivs->chn[ChnNum].sem_process_result, &ts);
	}

	if((ret < 0) && (timeoutMs != 0)) {
		IMP_LOG_ERR(TAG, "%s(%d):sem_timedwait() error [%s], ms=%d!\n", __func__, __LINE__, strerror(errno), timeoutMs);
	}

	return ret;
}

int IMP_IVS_GetResult(int ChnNum, void **result)
{
	IVS *ivs;

	if((ChnNum > NR_MAX_IVS_CHANNEL) || (ChnNum < 0)) {
		IMP_LOG_ERR(TAG, "ChnNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	if(ivs->chn[ChnNum].handler == NULL) {
		IMP_LOG_ERR(TAG, "%s:not init IMP_IVS_CreateChn!\n", __func__);
		return -1;
	}

	IMPIVSInterface *handler = ivs->chn[ChnNum].handler;

    if ((handler->getResult == NULL) || (handler->getResult(handler, result) < 0)) {
        IMP_LOG_ERR(TAG, "handler->getResult(%p) failed!\n", handler->getResult);
        return -1;
    }

	return 0;
}

int IMP_IVS_ReleaseResult(int ChnNum, void *result)
{
	IVS *ivs;

	if((ChnNum > NR_MAX_IVS_CHANNEL) || (ChnNum < 0)) {
		IMP_LOG_ERR(TAG, "ChnNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	if(ivs->chn[ChnNum].handler == NULL) {
		IMP_LOG_ERR(TAG, "%s:not init IMP_IVS_CreateChn!\n", __func__);
		return -1;
	}

	IMPIVSInterface *handler = ivs->chn[ChnNum].handler;

    if ((handler->releaseResult == NULL) || (handler->releaseResult(handler, result) < 0)) {
        IMP_LOG_ERR(TAG, "handler->releaseResult(%p) failed!\n", handler->releaseResult);
        return -1;
    }

	return 0;
}

int IMP_IVS_ReleaseData(void *virAddr)
{
	return VBMUnlockFrameByVaddr((uint32_t)virAddr);
}

int IMP_IVS_GetParam(int chnNum, void *param)
{
	IVS *ivs;

	if (param == NULL) {
		IMP_LOG_ERR(TAG, "param is NULL!\n");
		return -1;
	}

	if((chnNum > NR_MAX_IVS_CHANNEL) || (chnNum < 0)) {
		IMP_LOG_ERR(TAG, "ChnNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	if(ivs->chn[chnNum].handler == NULL) {
		IMP_LOG_ERR(TAG, "%s:not init IMP_IVS_CreateChn!\n", __func__);
		return -1;
	}

	IMPIVSInterface *handler = ivs->chn[chnNum].handler;

	return handler->getParam ? handler->getParam(handler, param) : -1;
}

int IMP_IVS_SetParam(int chnNum, void *param)
{
	IVS *ivs;

	if (param == NULL) {
		IMP_LOG_ERR(TAG, "param is NULL!\n");
		return -1;
	}

	if((chnNum > NR_MAX_IVS_CHANNEL) || (chnNum < 0)) {
		IMP_LOG_ERR(TAG, "ChnNum is error !\n");
		return -1;
	}

	ivs = GetIVS();
	if (ivs == NULL) {
		IMP_LOG_ERR(TAG, "ivs_create_group error !\n");
		return -1;
	}

	if(ivs->chn[chnNum].handler == NULL) {
		IMP_LOG_ERR(TAG, "%s:not init IMP_IVS_CreateChn!\n", __func__);
		return -1;
	}

	IMPIVSInterface *handler = ivs->chn[chnNum].handler;

    if ((handler->param != NULL) && (handler->paramSize != 0)) {
        memcpy(handler->param, param, handler->paramSize);
        ivs->chn[chnNum].set_param_flag = true;
    }

	return 0;
}
